mod config;
mod controller;
mod model;
mod service;
mod error;

use std::{io::Write, sync::Arc};

use axum::{http::header, Extension};
use config::Config;
use controller::todo_router;

pub struct AppState {
    pub config: Config,
    pub pool: sqlx::sqlite::SqlitePool,
}

#[tokio::main]
async fn main() {
    dotenvy::dotenv().ok();

    env_logger::Builder::from_default_env()
        .format(|buf, record| {
            let now = chrono::Local::now().format("%Y-%m-%d %H:%M:%S");
            writeln!(
                buf,
                "[{}][{:5}][{}] {}",
                now,
                record.level(),
                record.target(),
                record.args()
            )
        })
        .init();

    log::info!("☕️ Initializing configuration");
    let config = Config::init();

    let pool = match sqlx::sqlite::SqlitePoolOptions::new()
        .max_connections(4)
        .connect(&config.database_url)
        .await
    {
        Err(e) => {
            log::error!("🔥 Failed to connect to sqlite db: {}", e);
            std::process::exit(1);
        }
        Ok(pool) => {
            log::info!("✅️ Connect to sqlite db successfully");
            pool
        }
    };

    let app_state = Arc::new(AppState { pool, config });

    let cors = tower_http::cors::CorsLayer::new()
        .allow_origin(["http://127.0.0.1:5173".parse().unwrap()])
        .allow_headers([header::CONTENT_TYPE, header::ACCEPT])
        .allow_methods(tower_http::cors::Any);

    let app = todo_router()
        .layer(Extension(app_state.clone()))
        .layer(cors);

    let listener = tokio::net::TcpListener::bind(("0.0.0.0", app_state.config.port))
        .await
        .unwrap();
    log::info!("🎉 Server is running on 0.0.0.0:{}", app_state.config.port);

    axum::serve(listener, app).await.unwrap();
}
